package tr.edu.ege.kipler.satisfiability.completion;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import tr.edu.ege.kipler.satisfiability.ReasonerException;
import tr.edu.ege.kipler.satisfiability.Reasoner;
import tr.edu.ege.kipler.satisfiability.blocking.BlockingStrategy;
import tr.edu.ege.kipler.satisfiability.blocking.OptimizedSHIQABoxBlocking;
import tr.edu.ege.kipler.satisfiability.completion.rules.CompletionRule;
import tr.edu.ege.kipler.satisfiability.completion.rules.EmptyDomainRule;
import tr.edu.ege.kipler.satisfiability.completion.rules.dl.ABoxRule;
import tr.edu.ege.kipler.satisfiability.completion.rules.dl.AllValuesRule;
import tr.edu.ege.kipler.satisfiability.completion.rules.dl.AtLeastRule;
import tr.edu.ege.kipler.satisfiability.completion.rules.dl.AtMostRule;
import tr.edu.ege.kipler.satisfiability.completion.rules.dl.ChooseRule;
import tr.edu.ege.kipler.satisfiability.completion.rules.dl.IntersectionRule;
import tr.edu.ege.kipler.satisfiability.completion.rules.dl.LazyUnfoldingRule;
import tr.edu.ege.kipler.satisfiability.completion.rules.dl.NegatedConceptDefinitionRule;
import tr.edu.ege.kipler.satisfiability.completion.rules.dl.NegatedRoleInclusionRule;
import tr.edu.ege.kipler.satisfiability.completion.rules.dl.PositiveConceptDefinitionRule;
import tr.edu.ege.kipler.satisfiability.completion.rules.dl.SHIQABoxBothRootMergeStrategy;
import tr.edu.ege.kipler.satisfiability.completion.rules.dl.SHIQABoxNotBothRootMergeStrategy;
import tr.edu.ege.kipler.satisfiability.completion.rules.dl.SomeValuesRule;
import tr.edu.ege.kipler.satisfiability.completion.rules.dl.TransitivityRule;
import tr.edu.ege.kipler.satisfiability.completion.rules.dl.UnionRule;
import tr.edu.ege.kipler.satisfiability.container.Individual;
import tr.edu.ege.kipler.satisfiability.container.ModifiedIndividualCollection;
import tr.edu.ege.kipler.satisfiability.container.World;
import tr.edu.ege.kipler.satisfiability.container.WorldConstraintSystem;
import tr.edu.ege.kipler.satisfiability.container.operational.FilterType;
import tr.edu.ege.kipler.satisfiability.container.operational.RelatedIndividualWithTypeFilter;
import tr.edu.ege.kipler.satisfiability.container.operational.RelatedIndividualWithTypeFilterFactory;

/**
 * 
 * @author İnanç Seylan
 * 
 */
public class SHIQModifiedIndividualBasedCompletionStrategy implements
		CompletionStrategy {

	private List<ABoxRule> generatingRuleList = new ArrayList<ABoxRule>();

	private List<ABoxRule> shrinkingRuleList = new ArrayList<ABoxRule>();

	private List<ABoxRule> labelModifyingRuleList = new ArrayList<ABoxRule>();

	private Reasoner reasoner;

	private World currentWorld;

	private WorldConstraintSystem currentWcs;

	public SHIQModifiedIndividualBasedCompletionStrategy(Reasoner reasoner) {
		setReasoner(reasoner);
		BlockingStrategy blocking = new OptimizedSHIQABoxBlocking();
		RelatedIndividualWithTypeFilter filterer = RelatedIndividualWithTypeFilterFactory
				.getFilter(FilterType.TYPED_NEIGHBOR);
		labelModifyingRuleList.add(new LazyUnfoldingRule());
		labelModifyingRuleList.add(new PositiveConceptDefinitionRule(blocking));
		labelModifyingRuleList.add(new IntersectionRule(blocking));
		labelModifyingRuleList.add(new AllValuesRule(blocking));
		labelModifyingRuleList.add(new TransitivityRule(blocking));
		labelModifyingRuleList.add(new UnionRule(getReasoner(), blocking));
		labelModifyingRuleList.add(new ChooseRule(getReasoner(), blocking));

		shrinkingRuleList.add(new AtMostRule(getReasoner(), blocking,
				new SHIQABoxBothRootMergeStrategy()));
		shrinkingRuleList.add(new AtMostRule(getReasoner(), blocking,
				new SHIQABoxNotBothRootMergeStrategy()));

		generatingRuleList.add(new SomeValuesRule(blocking, filterer, false));
		generatingRuleList.add(new AtLeastRule(blocking, filterer, false));
	}

	/**
	 * @return the reasoner
	 */
	public Reasoner getReasoner() {
		return reasoner;
	}

	/**
	 * @param reasoner
	 *            the reasoner to set
	 */
	public void setReasoner(Reasoner reasoner) {
		this.reasoner = reasoner;
	}

	public void apply(WorldConstraintSystem currentWcs)
			throws ReasonerException {
		currentWorld = currentWcs.getAllWorlds().iterator().next();
		this.currentWcs = currentWcs;
		CompletionRule compRule = new NegatedRoleInclusionRule(currentWcs,
				currentWorld);
		compRule.apply();
		compRule = new NegatedConceptDefinitionRule(currentWcs, currentWorld);
		compRule.apply();
		compRule = new EmptyDomainRule(currentWcs, currentWorld);
		compRule.apply();
		do {
			applyLabelModifyingRules();
		} while (applyShrinkingRules() || applyGeneratingRules());
	}

	private void applyLabelModifyingRules() throws ReasonerException {
		boolean ruleApplied;
		do {
			ruleApplied = false;
			Set<String> iterationCopy = new HashSet<String>(getModIndvList()
					.get(currentWorld.getName()));
			for (String indvName : iterationCopy) {
				Individual indv = currentWorld.getABox()
						.getIndividual(indvName);
				while (applyARuleFromRuleList(labelModifyingRuleList, indv)) {
					ruleApplied = true;
				}
			}
		} while (ruleApplied);
	}

	private boolean applyShrinkingRules() throws ReasonerException {
		return applyARuleFromRuleList(shrinkingRuleList, false);
	}

	private boolean applyGeneratingRules() throws ReasonerException {
		return applyARuleFromRuleList(generatingRuleList, true);
	}

	private boolean applyARuleFromRuleList(List<ABoxRule> ruleList,
			boolean removeIfNonApplicable) throws ReasonerException {
		boolean ruleApplied = false;
		Set<String> removeSet = new HashSet<String>();
		Iterator<String> i = getModIndvList().get(currentWorld.getName())
				.iterator();
		while (i.hasNext() && !ruleApplied) {
			String indvName = i.next();
			Individual indv = currentWorld.getABox().getIndividual(indvName);
			ruleApplied = applyARuleFromRuleList(ruleList, indv);
			if (!ruleApplied && removeIfNonApplicable) {
				removeSet.add(indvName);
			}
		}
		for (String indvName : removeSet) {
			getModIndvList().remove(currentWorld.getName(), indvName);
		}
		return ruleApplied;
	}

	private boolean applyARuleFromRuleList(List<ABoxRule> ruleList,
			Individual indv) throws ReasonerException {
		boolean ruleApplied = false;
		Iterator<ABoxRule> ruleIter = ruleList.iterator();
		while (ruleIter.hasNext() && !ruleApplied) {
			ABoxRule rule = ruleIter.next();
			rule.setWcs(currentWcs);
			rule.setWorld(currentWorld);
			rule.setIndividual(indv);
			rule.apply();
			ruleApplied = rule.isApplied();
		}
		return ruleApplied;
	}

	private ModifiedIndividualCollection getModIndvList() {
		return currentWcs.getData().getModifiedIndividuals();
	}
}
